`include "global_def.h"

module Execute(
  I_CLOCK,
  I_LOCK,
  I_PC,
  I_Opcode,
  I_Src1Value,
  I_Src2Value,
  I_DestRegIdx,
  I_Imm,
  I_FetchStall,
  I_DepStall,
  I_VectorSrc1Value, //New Inputs
  I_VectorSrc2Value,
  I_VectorDestRegIdx,
  I_Idx, //End New Inputs
  I_RAST_STALL,
  O_LOCK,
  O_ALUOut,
  O_Opcode,
  O_DestRegIdx,
  O_DestValue,
  O_VectorALUOut, //New Outputs
  O_VectorDestRegIdx, //End New Outputs
  O_FetchStall,
  O_DepStall
);

/////////////////////////////////////////
// IN/OUT DEFINITION GOES HERE
/////////////////////////////////////////
//
// Inputs from the decode stage
input I_CLOCK;
input I_LOCK;
input signed [`PC_WIDTH-1:0] I_PC;
input [`OPCODE_WIDTH-1:0] I_Opcode;
input [3:0] I_DestRegIdx;
input signed [`REG_WIDTH-1:0] I_Src1Value;
input signed [`REG_WIDTH-1:0] I_Src2Value;
input signed [`REG_WIDTH-1:0] I_Imm;
input I_FetchStall;
input I_DepStall;
input [`VREG_WIDTH-1:0] I_VectorSrc1Value; //New
input [`VREG_WIDTH-1:0] I_VectorSrc2Value; //New
input [5:0] I_VectorDestRegIdx;
input [1:0] I_Idx;
input I_RAST_STALL; //Stall from Rasterisation


// Outputs to the memory stage
output reg O_LOCK;
output reg [`REG_WIDTH-1:0] O_ALUOut;
output reg [`OPCODE_WIDTH-1:0] O_Opcode;
output reg [3:0] O_DestRegIdx;
output reg [`REG_WIDTH-1:0] O_DestValue;
output reg O_FetchStall;
output reg O_DepStall;
output reg [`VREG_WIDTH-1:0] O_VectorALUOut;
output reg [5:0] O_VectorDestRegIdx;

/////////////////////////////////////////
// WIRE/REGISTER DECLARATION GOES HERE
/////////////////////////////////////////
//

/////////////////////////////////////////
// ALWAYS STATEMENT GOES HERE
/////////////////////////////////////////
//

/////////////////////////////////////////
// ## Note ##
// - Do the appropriate ALU operations.
//////////////////////////

///////////////
wire [15:0] tmp_ALU;
wire [15:0] tmp_DestValue;

assign tmp_ALU = 
		(((I_LOCK == 1'b1) && ((!I_FetchStall) && (!I_RAST_STALL))) ? ((I_DepStall == 1'b0) ?
      ((I_Opcode == `OP_ADD_D) ? (I_Src1Value + I_Src2Value) :
       (I_Opcode == `OP_ADDI_D) ? (I_Src1Value + I_Imm) :
		 (I_Opcode == `OP_AND_D) ? (I_Src1Value & I_Src2Value) :
		 (I_Opcode == `OP_ANDI_D) ? (I_Src1Value & I_Imm) :
		 (I_Opcode == `OP_MOV) ? (I_Src1Value) :
       (I_Opcode == `OP_MOVI_D) ? (I_Imm) :
		 (I_Opcode == `OP_BRN) ? (I_PC + (I_Imm << 2)) :
		 (I_Opcode == `OP_BRZ) ? (I_PC + (I_Imm << 2)) :
		 (I_Opcode == `OP_BRP) ? (I_PC + (I_Imm << 2)) :
		 (I_Opcode == `OP_BRNZ) ? (I_PC + (I_Imm << 2)) :
		 (I_Opcode == `OP_BRNP) ? (I_PC + (I_Imm << 2)) :
		 (I_Opcode == `OP_BRZP) ? (I_PC + (I_Imm << 2)) :
		 (I_Opcode == `OP_BRNZP) ? (I_PC + (I_Imm << 2)) :
		 (I_Opcode == `OP_JSR) ? (I_PC + (I_Imm << 2)) :
		 (I_Opcode == `OP_JSRR) ? (I_Src1Value) :
		 (I_Opcode == `OP_JMP) ? (I_Src1Value) :
		 (I_Opcode == `OP_RET) ? (I_Src1Value) :
		 (I_Opcode == `OP_STW) ? (I_Src1Value) : 
       (16'h0000)
      ) : (16'h0000)
    ) : (16'h0000));
	 
	 
assign tmp_DestValue = 
		(((I_LOCK == 1'b1) && ((!I_FetchStall) && (!I_RAST_STALL))) ? ((I_DepStall == 1'b0) ?
      ((I_Opcode == `OP_LDW) ? ((I_Src1Value + I_Imm)) :
       (I_Opcode == `OP_STW) ? ((I_Src2Value + I_Imm)) :
       (16'h0000)
      ) : (16'h0000)
    ) : (16'h0000));


//wire [15:0] temp_VADD0;
//wire [15:0] temp_VADD1;
//wire [15:0] temp_VADD2;
//wire [15:0] temp_VADD3;
//
//wire [15:0] vsrc1_0;
//wire [15:0] vsrc1_1;
//wire [15:0] vsrc1_2;
//wire [15:0] vsrc1_3;
//wire [15:0] vsrc2_0;
//wire [15:0] vsrc2_1;
//wire [15:0] vsrc2_2;
//wire [15:0] vsrc2_3;
//
//assign temp_VADD0 = (

//assign tmp_VALU = 
//		(((I_LOCK == 1'b1) && (I_FetchStall == 1'b0)) ? ((I_DepStall == 1'b0) ?
//      ((I_Opcode == `OP_ADD_D) ? (I_Src1Value + I_Src2Value) :
//       (I_Opcode == `OP_ADDI_D) ? (I_Src1Value + I_Imm) :
//		 (I_Opcode == `OP_VADD) ? ( ):
//				
//       (16'h0000)
//      ) : (16'h0000)
//    ) : (16'h0000));
// ............................oh god, don't make me do it
	 

reg [63:0] temp_VALU;

always @ ( * )
begin //VADD, VMOV, VMOVI, VCOMPMOV, VCOMPMOVI
		if (I_Opcode == `OP_VADD) begin //VADD:  Add 4 16-bit registers in 1.8.7 format
			//For each 16-bit reg, if sign bit is the same, add;
			//      else, subtract, and sign is of the larger magnitude of the two numbers
			
			//********1st dest[0] = src1[0] + src2[0] (temp_VALU[15:0] = I_VectorSrc1Value[15:0] + I_VectorSrc2Value[15:0])********
			if (I_VectorSrc1Value[15] ^ I_VectorSrc2Value[15]) begin //The signs are different; need to figure out which magnitude is larger
				if(I_VectorSrc1Value[14:0] > I_VectorSrc2Value[14:0]) begin //reg 1 is larger, its sign will dominate
					temp_VALU[15] = I_VectorSrc1Value[15];
					temp_VALU[14:0] = (I_VectorSrc1Value[14:0] - I_VectorSrc2Value[14:0]);			
				end
				else if(I_VectorSrc1Value[14:0] < I_VectorSrc2Value[14:0]) begin //reg 2 is larger, its sign will dominate
					temp_VALU[15] = I_VectorSrc2Value[15];
					temp_VALU[14:0] = (I_VectorSrc2Value[14:0] - I_VectorSrc1Value[14:0]);
				end
				else begin //They're equal
					temp_VALU[15:0] = 16'b0;
				end
			end
			else begin //Signs are the same, just add
				temp_VALU[15] = I_VectorSrc1Value[15];
				temp_VALU[14:0] = (I_VectorSrc1Value[14:0] + I_VectorSrc2Value[14:0]);
			end
			
			
			//*********2nd dest[1] = src1[1] + src2[1]********
			if (I_VectorSrc1Value[31] ^ I_VectorSrc2Value[31]) begin //The signs are different; need to figure out which magnitude is larger
				if(I_VectorSrc1Value[30:16] > I_VectorSrc2Value[30:16]) begin //reg 1 is larger, its sign will dominate
					temp_VALU[31] = I_VectorSrc1Value[31];
					temp_VALU[30:16] = (I_VectorSrc1Value[30:16] - I_VectorSrc2Value[30:16]);			
				end
				else if(I_VectorSrc1Value[30:16] < I_VectorSrc2Value[30:16]) begin //reg 2 is larger, its sign will dominate
					temp_VALU[31] = I_VectorSrc2Value[31];
					temp_VALU[30:16] = (I_VectorSrc2Value[30:16] - I_VectorSrc1Value[30:16]);
				end
				else begin //They're equal
					temp_VALU[31:16] = 16'b0;
				end
			end
			else begin //Signs are the same, just add
				temp_VALU[31] = I_VectorSrc1Value[31];
				temp_VALU[30:16] = (I_VectorSrc1Value[30:16] + I_VectorSrc2Value[30:16]);
			end
			
			//*********3rd dest[2] = src1[2] + src2[2]********
			if (I_VectorSrc1Value[47] ^ I_VectorSrc2Value[47]) begin //The signs are different; need to figure out which magnitude is larger
				if(I_VectorSrc1Value[46:32] > I_VectorSrc2Value[46:32]) begin //reg 1 is larger, its sign will dominate
					temp_VALU[47] = I_VectorSrc1Value[47];
					temp_VALU[46:32] = (I_VectorSrc1Value[46:32] - I_VectorSrc2Value[46:32]);			
				end
				else if(I_VectorSrc1Value[46:32] < I_VectorSrc2Value[46:32]) begin //reg 2 is larger, its sign will dominate
					temp_VALU[47] = I_VectorSrc2Value[47];
					temp_VALU[46:32] = (I_VectorSrc2Value[46:32] - I_VectorSrc1Value[46:32]);
				end
				else begin //They're equal
					temp_VALU[47:32] = 16'b0;
				end
			end
			else begin //Signs are the same, just add
				temp_VALU[47] = I_VectorSrc1Value[47];
				temp_VALU[46:32] = (I_VectorSrc1Value[46:32] + I_VectorSrc2Value[46:32]);
			end
			
			//*********4th dest[3] = src1[3] + src2[3]********
			if (I_VectorSrc1Value[63] ^ I_VectorSrc2Value[63]) begin //The signs are different; need to figure out which magnitude is larger
				if(I_VectorSrc1Value[62:48] > I_VectorSrc2Value[62:48]) begin //reg 1 is larger, its sign will dominate
					temp_VALU[63] = I_VectorSrc1Value[63];
					temp_VALU[62:48] = (I_VectorSrc1Value[62:48] - I_VectorSrc2Value[62:48]);			
				end
				else if(I_VectorSrc1Value[62:48] < I_VectorSrc2Value[62:48]) begin //reg 2 is larger, its sign will dominate
					temp_VALU[63] = I_VectorSrc2Value[63];
					temp_VALU[62:48] = (I_VectorSrc2Value[62:48] - I_VectorSrc1Value[62:48]);
				end
				else begin //They're equal
					temp_VALU[63:48] = 16'b0;
				end
			end
			else begin //Signs are the same, just add
				temp_VALU[63] = I_VectorSrc1Value[63];
				temp_VALU[62:48] = (I_VectorSrc1Value[62:48] + I_VectorSrc2Value[62:48]);
			end
		end //END VADD
		
		else if (I_Opcode == `OP_VMOV) begin //VMOV:  dest<- src
			temp_VALU[63:0] = I_VectorSrc1Value[63:0];	
		end //END VMOV
		
		else if (I_Opcode == `OP_VMOVI) begin //VMOVI: dest[0],dest[1],dest[2],dest[3] <= I_Imm
			temp_VALU[15:0] = I_Imm;
			temp_VALU[31:16] = I_Imm;
			temp_VALU[47:32] = I_Imm;
			temp_VALU[63:48] = I_Imm;
		end //END VMOVI
		
		else if (I_Opcode == `OP_VCOMPMOV) begin //VCOMPMOV: dest[idx] <- src
			if (I_Idx == 0) begin
				temp_VALU[63:16] = I_VectorSrc1Value[63:16];
				temp_VALU[15:0] = I_Src1Value[15:0];		
			end
			else if(I_Idx == 1) begin
				temp_VALU[63:32] = I_VectorSrc1Value[63:32];
				temp_VALU[31:16] = I_Src1Value[15:0];
				temp_VALU[15:0] = I_VectorSrc1Value[15:0];
			end
			else if(I_Idx == 2) begin
				temp_VALU[63:48] = I_VectorSrc1Value[63:48];
				temp_VALU[47:32] = I_Src1Value[15:0];
				temp_VALU[31:0] = I_VectorSrc1Value[31:0];		
			end
			else /*if(I_Idx == 3)*/ begin
				temp_VALU[63:48] = I_Src1Value[15:0];
				temp_VALU[47:0] = I_VectorSrc1Value[47:0];
			end		
		end //END VCOMPMOV
		
		else if (I_Opcode == `OP_VCOMPMOVI) begin //VCOMPMOVI: dest[idx] <- src
			if (I_Idx == 0) begin
				temp_VALU[63:16] = I_VectorSrc1Value[63:16];
				temp_VALU[15:0] = I_Imm[15:0];		
			end
			else if(I_Idx == 1) begin
				temp_VALU[63:32] = I_VectorSrc1Value[63:32];
				temp_VALU[31:16] = I_Imm[15:0];
				temp_VALU[15:0] = I_VectorSrc1Value[15:0];
			end
			else if(I_Idx == 2) begin
				temp_VALU[63:48] = I_VectorSrc1Value[63:48];
				temp_VALU[47:32] = I_Imm[15:0];
				temp_VALU[31:0] = I_VectorSrc1Value[31:0];		
			end
			else /*if(I_Idx == 3)*/ begin
				temp_VALU[63:48] = I_Imm[15:0];
				temp_VALU[47:0] = I_VectorSrc1Value[47:0];
			end	
		end //END VCOMPMOVI
		
		else if ((I_Opcode == `OP_SETVERTEX) || (I_Opcode == `OP_SETCOLOR) || (I_Opcode == `OP_BEGINPRIMITIVE) || (I_Opcode == `OP_ROTATE) || (I_Opcode == `OP_TRANSLATE) || (I_Opcode == `OP_SCALE)) begin
			temp_VALU = I_VectorSrc1Value;
		end

		else begin
			temp_VALU = 64'b0;
		end	
end

	 

always @(negedge I_CLOCK)
begin
	if (!I_RAST_STALL) begin
	  O_LOCK <= I_LOCK;
	  O_FetchStall <= I_FetchStall;
	  O_DepStall <= I_DepStall;
	  
	  if ((I_LOCK == 1'b1) && (I_FetchStall == 1'b0) && (I_DepStall == 1'b0) )
	  begin
		O_Opcode <= I_Opcode;
		O_DestRegIdx <= I_DestRegIdx;
		O_VectorDestRegIdx <= I_VectorDestRegIdx;
		
		O_DestValue <= tmp_DestValue;
		O_ALUOut <= tmp_ALU;
		O_VectorALUOut <= temp_VALU;
	  end // if (I_LOCK == 1'b1)
	  
	  else begin //I_LOCK == 1'b0
		O_ALUOut <= 16'h0;
		O_DestValue <= 16'h0;
		O_VectorALUOut <= 64'h0;
	  end
	  
	end // if (!I_RAST_STALL)
end // always @(negedge I_CLOCK)
endmodule // module Execute
